#!/usr/bin/env python3
from tkinter import *
import easygui,os
from hashlib import sha1
from threading import Thread,Timer

# 导入自定义库
import share_db
import share_net
from tk_ext import  MultiListbox
from net_conf import max_file_size
# end
# xmpp 连接线程，每个服务器一个线程
client_threads = []
#定时注册间隔
reg_interval = 3500
quit_flag = False
app = None

def reg_clients():
    global client_threads, checker,reg_interval,quit_flag,app
    if quit_flag == True:
        return
    #改为未连接
    for i in range(app.server_list.size()):
        item = app.server_list.get(i)
        app.server_list.delete(i)
        app.server_list.insert(i,(item[0],item[1],'未连接'))
        self.server_list.itemconfig(i,bg='white')
    for k in share_net.status:
        share_net.status[k]=False
    #重新登录reg
    for t in client_threads:
        share_net.send_reg( t.client,t.user )
    checker = Timer( reg_interval ,reg_clients )
    checker.start()
    
checker = Timer(reg_interval, reg_clients)

def file_hash(filename):
    """ sha,size = file_hash( pathname ) """
    try:
        size = os.path.getsize(filename)
    except:
        return (None,0)
    if size>max_file_size:
        return (None,0)
    data=[]
    fp = open(filename,'rb')
    for line1 in fp:
        data.append(line1)
    fp.close()
    dat = b''.join(data)
    sha = sha1( dat )
    return (sha.hexdigest(),size)
    
class init_proccess(Thread):
    """  初始化： XMPP 连接、 共享文件显示（需要计算MD5）  """
    def __init__(self, win):
        super().__init__()
        self.win = win
    def run(self):
        """ 初始化顺序： 1、服务器、共享文件显示。 2、连接服务器 """
        #显示
        isize = self.win.server_list.size()
        for i in range(isize):
            item = self.win.server_list.get(i)
        for item in share_db.get_shared():
            sha,size = file_hash( item[0] )
            if size==0:
                share_db.del_shared(item[1])
                continue
            self.win.share_details[item[1]] = (item[0],sha,size)
            self.win.share_list.insert(END,item[1])
        #连接
        global client_threads
        users = share_net.get_users()
        for usr in users:
            t = share_net.XmppClient(usr, self.win)
            t.start()
            client_threads.append(t)
        #定时注册
        global checker
        checker.start()
        return
        
class MainWindow(PanedWindow):
    """ file share system client on xmpp protocol """
    share_details = {}
    #本地共享列表： share_details[sharename] = (path,sha1,size)
    down_files = {}
    #正在下载的： down_files[ sharename ] = index
    def __init__(self,master=None):
        super().__init__(master)
        self.config(width=800,height=600)
        self.pack(fill=BOTH,expand=1)
        
        frame_L = Frame(self)
        frame_R = Frame(self)
        
        #layout frame_L and fram_R
        self.add(frame_L)
        self.add(frame_R)
        
        #on left frame
        self.server_btn = Button(frame_L)
        self.server_list = MultiListbox(frame_L,[('服务器',20),('端口',5),('状态',6)])
        self.share_btn = Button(frame_L)
        self.share_list = Listbox(frame_L)
        
        self.server_btn['text'] = '增加服务器'
        self.server_btn['command'] = self.add_server
        self.server_btn.pack(side='top')
        
        self.server_list.pack(side='top',fill=BOTH,expand=1)
        self.server_list.bind2('<Double-Button-1>',self.lb_dbclick)
        
        self.share_btn['text'] = '增加共享文件'
        self.share_btn['command'] = self.add_sharefile
        self.share_btn.pack(side='top')
        
        self.share_list.pack(side='top',fill=BOTH,expand=1)
        self.share_list.bind('<Double-Button-1>',self.del_sharefile)
        
        #self.hi_there["text"] = "Hello World\n(click me)"
        #self.hi_there["command"] = self.say_hi
        #self.hi_there.pack(side="top")
        #on right frame
        frame_R_up = Frame( frame_R ,height=50)
        self.recv_list = MultiListbox(frame_R, [('文件名',45),('sha值',20),('大小',15),('来源',15)] )
        
        frame_R_up.pack(side='top',fill=X)
        self.recv_list.pack(side='top',fill=BOTH,expand=1)
        self.recv_list.bind2('<Double-Button-1>',self.res_dbclick)
        self.recv_list.bind2('<Button-3>',self.res_un_select)
        self.recv_list.bind2('<Button-2>',self.res_un_select)
        
        key_label = Label(frame_R_up,text='关键字：')
        self.key_entry = Entry(frame_R_up)
        self.search_btn = Button(frame_R_up)

        key_label.pack(side='left')
        self.key_entry.pack(side='left',fill=X,expand=1)
        self.key_entry.bind('<Return>',self.btn_search)
        
        self.search_btn['text'] = '搜索'
        self.search_btn['command'] = self.btn_search
        self.search_btn.pack(side='left')
        
        self.bind('<Destroy>',self.on_destroy)
        #init server list and shared list
        self.lists_init()

    def action_none(self,event=None):
        print( 'Nothing %s'%event )
        
    def btn_search(self,event=None):
        global client_threads
        if len(self.down_files)>0:
            easygui.msgbox( title='暂时不能搜索',msg='正在下载，不能重新搜索，请等待下载结束')
            return
        self.recv_list.delete(0, END )
        key = self.key_entry.get().strip()
        if len(key)<2:
            return
        for t in client_threads:
            share_net.send_want(t.client,t.user, key )
        return
    def server_match( self, user , to_jid ):
        m = '@' + user[3] + '/'
        jid = to_jid.decode()
        try:
            jid.index(m)
            return True
        except:
            return False
    def get_share_path(self,sharename):
        return self.share_details[sharename][0]
    def res_un_select(self,event=None):
        #print('clear selection')
        self.recv_list.selection_clear(0,END)
    def res_dbclick(self,event=None):
        idx = self.recv_list.curselection()[0]
        item = self.recv_list.get(idx)
        if self.down_files.get( item[0] ) is not None:
            easygui.msgbox(msg='正在下载，不可重复', title='警告')
            return
        if self.share_exists(item[0]):
            easygui.msgbox(msg='你有同样的共享文件，不可下载', title='警告')
            return
        if os.path.exists( 'Downloads/'+item[0]):
            yn = easygui.ynbox(msg='你有未共享的同名文件，是否覆盖？', title='警告', choices=('确定', '不要'))
            if yn==0:
                return
        else:
            yn = easygui.ynbox(msg='是否下载“%s”文件？'%item[0], title='选择', choices=('确定', '不要'))
        if yn==1:
            for t in client_threads:
                if self.server_match( t.user, item[3] ):
                    share_net.send_select( t.client,item )
                    break
            self.recv_list.itemconfig( idx,bg='red' )
            self.down_files[ item[0] ] = idx
            self.recv_list.selection_clear(0,END)
        
    def lb_dbclick(self,event=None):
        idx = self.server_list.curselection()[0]
        #print(idx)
        item = self.server_list.get(idx)
        yn = easygui.ynbox(msg='是否删除 %s 服务器？'%item[0], title='选择', choices=('确定', '不要'))
        if yn==1:
            self.server_list.delete(idx)
            share_db.del_server(item[0])
        
    def lists_init(self):
        for item in share_db.get_servers():
            self.server_list.insert(END, item)
            #print(item[0],item[1],item[2])
        self.thread1 = init_proccess(self)
        self.thread1.start()
        
    def add_server(self):
        msg = '输入服务器地址和端口号'
        title = '服务器地址和端口号'
        fieldnames = ['服务器:','端口号:']
        fieldvalues = ['localhost','5222']
        fieldvalues = easygui.multenterbox(msg,title,fieldnames,fieldvalues)
        if fieldvalues != None:
            fieldvalues.append('未连接')
            fieldvalues[0] = fieldvalues[0].strip()
            isize = self.server_list.size()
            for i in range(isize):
                item = self.server_list.get(i)
                if item[0]==fieldvalues[0]:
                    return None
            try:
                fieldvalues[1] = int(fieldvalues[1])
                self.server_list.insert(END,fieldvalues)
                share_db.add_server(fieldvalues[0],fieldvalues[1])
            except:
                pass
                
    def share_exists(self,sharename):
        sz = self.share_list.size()
        res = False
        for item in self.share_list.get(0,sz-1):
            if item==sharename:
                res = True
                break
        return res
            
    def del_sharefile(self,event=None):
        idx = self.share_list.curselection()[0]
        sharename = self.share_list.get(idx)
        yn = easygui.ynbox( msg='是否不再共享文件： %s ？'%sharename, title='选择', choices=('确定', '不要') )
        if yn==0:
            return
        self.share_list.delete(idx)
        share_db.del_shared(sharename)
        self.share_details.pop(sharename)
    def add_sharefile(self):
        filename = easygui.fileopenbox(title='选择要共享的文件')
        if filename is None:
            return
        #print(filename)
        sharename = easygui.enterbox(title = '该文件的共享名称',default = os.path.split(filename)[1])
        sharename = sharename.strip()
        if self.share_exists(sharename):
            easygui.msgbox(title='不能加入',msg='该该共享名已存在，不可再添加')
            return
        sha,size = file_hash(filename)
        if size==0:
            return None
        self.share_details[sharename] = (filename,sha,size)
        self.share_list.insert(END,sharename)
        share_db.add_shared( filename,sharename )
    def on_destroy(self,event=None):
        global client_threads
        for t in client_threads:
            if share_net.status[ t.user[0] ] == False:
                break
            share_net.send_quit( t.client, t.user)
            t.client.disconnect()
        #防止被 Timer 卡死
        global checker,quit_flag
        quit_flag = True
        checker.cancel()
        self.destroy()
        return 0
       
    def status_download( self,sharename ):
        idx = self.down_files.pop( sharename )
        file_desc = self.recv_list.get(idx)
        path = os.getcwd()+'/Downloads/'+file_desc[0]
        ha = file_hash(path)
        if ha[0]==file_desc[1] and ha[1]==file_desc[2]:
            self.share_details[file_desc[0]] = (path,file_desc[1],file_desc[2])
            self.share_list.insert(END,file_desc[0])
            share_db.add_shared( path,file_desc[0] )
            self.recv_list.itemconfig( idx,bg='blue' )
        else:
            print('file not correct \n %s,%s\n %s,%s'%(ha[0],ha[1],file_desc[1],file_desc[2]))
            self.recv_list.itemconfig( idx,bg='white' )
        
    def status_connect(self,user):
        #user = (jid-0,password-1,srv_jid-2,domain-3,port-4)
        for i in range(self.server_list.size()):
            item = self.server_list.get(i)
            if item[0] == user[3]:
                self.server_list.delete(i)
                self.server_list.insert( i,(item[0],item[1],'已连接') )
                try:
                    self.server_list.itemconfig(i,bg='green')
                except Exception as e:
                    print(e)
                break
        return
    def status_disconnect(self,user):
        #user = (jid-0,password-1,srv_jid-2,domain-3,port-4)
        for i in range(self.server_list.size()):
            item = self.server_list.get(i)
            if item[0] == user[3]:
                self.server_list.delete(i)
                self.server_list.insert( i,(item[0],item[1],'未连接') )
                self.server_list.itemconfig(i,bg='white')
                break
        return
    def show_have(self,cmd,from_jid):
        for item in cmd['key']:
            list_item = (item[0],item[1],item[2],from_jid)
            self.recv_list.insert(END,list_item)
        return

if __name__=='__main__':
    root = Tk()
    root.title('分布式文件搜索客户端')
    root.minsize(800, 600)
    app = MainWindow(master=root)
    app.mainloop()
